package com.arpa.oms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.PartyGroupCache;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.oms.domain.dto.OmsOutboundDTO;
import com.arpa.oms.domain.dto.OmsOutboundItemDTO;
import com.arpa.oms.domain.entity.*;
import com.arpa.oms.domain.enums.OmsOutboundStatusEnum;
import com.arpa.oms.domain.enums.OmsOutboundTypeEnum;
import com.arpa.oms.domain.vo.*;
import com.arpa.oms.mapper.OmsOutboundMapper;
import com.arpa.oms.service.*;
import com.arpa.wms.cache.CustomerCache;
import com.arpa.wms.cache.WarehouseCache;
import com.arpa.wms.cache.WarehouseShipmentCache;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.DeliveryTypeEnum;
import com.arpa.wms.service.IGoodsService;
import com.arpa.wms.service.IOutboundItemService;
import com.arpa.wms.service.IPutawayItemService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.NumConst.NUM_ONE;
import static com.arpa.wms.domain.consts.NumConst.NUM_ZERO;

/**
 * <p>
 * OMS出库单 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-11-07
 */
@Service
public class OmsOutboundServiceImpl extends ServiceImpl<OmsOutboundMapper, OmsOutbound> implements IOmsOutboundService {
    public static final BigDecimal DECIMAL100 = new BigDecimal("100");
    private final PartyCache partyCache;

    private final IOmsOutboundItemService iOmsOutboundItemService;

    private final ISaleService iSaleService;

    private final ISaleItemService iSaleItemService;

    private final IOutboundItemService iOutboundItemService;

    private final IOmsInventoryService iOmsInventoryService;

    private final IGoodsService iGoodsService;

    private final IOmsSaleReturnService iOmsSaleReturnService;

    private final IOmsSaleReturnItemService iOmsSaleReturnItemService;

    private final WarehouseCache warehouseCache;

    private final IOmsSettlementService iOmsSettlementService;

    private final PartyGroupCache partyGroupCache;

    private final CustomerCache customerCache;

    @Autowired
    private WarehouseShipmentCache warehouseShipmentCache;

    private final IPutawayItemService putawayItemService;

    public OmsOutboundServiceImpl(PartyCache partyCache, IOmsOutboundItemService iOmsOutboundItemService,
                                  @Lazy ISaleService iSaleService, ISaleItemService iSaleItemService, IOutboundItemService iOutboundItemService,
                                  IOmsInventoryService iOmsInventoryService, @Lazy IGoodsService iGoodsService,
                                  @Lazy IOmsSaleReturnService iOmsSaleReturnService, IOmsSaleReturnItemService iOmsSaleReturnItemService,
                                  @Lazy IOmsSettlementService iOmsSettlementService,
                                  PartyGroupCache partyGroupCache,
                                  WarehouseCache warehouseCache,
                                  CustomerCache customerCache, IPutawayItemService putawayItemService){
        this.partyCache = partyCache;
        this.iOmsOutboundItemService = iOmsOutboundItemService;
        this.iSaleService = iSaleService;
        this.iSaleItemService = iSaleItemService;
        this.iOutboundItemService = iOutboundItemService;
        this.iOmsInventoryService = iOmsInventoryService;
        this.iGoodsService = iGoodsService;
        this.iOmsSaleReturnService = iOmsSaleReturnService;
        this.iOmsSaleReturnItemService = iOmsSaleReturnItemService;
        this.warehouseCache = warehouseCache;
        this.iOmsSettlementService = iOmsSettlementService;
        this.partyGroupCache = partyGroupCache;
        this.customerCache = customerCache;
        this.putawayItemService = putawayItemService;
    }
    /**
     * 保存
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor={Exception.class})
    public boolean save(OmsOutbound entity) {
        if(StringUtils.isBlank(entity.getCode())){
            entity.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.CK, entity.getShipmentCode(),UserUtil.getBranchCode()));
        }
        if(StringUtils.isBlank(entity.getCreatedBy())){
            entity.setCreatedBy(UserUtil.getCode());
            entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
        }
        entity.setDeleted(NUM_ZERO);
        return super.save(entity);
    }

    /**
     * 根据code更新实体
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor={Exception.class})
    public int updateByCode(OmsOutbound entity) {
        if(StringUtils.isBlank(entity.getModifiedBy())){
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        return baseMapper.update(entity,new QueryWrapper<OmsOutbound>().lambda().eq(OmsOutbound::getCode,entity.getCode()));
    }

    /**
     * 查询列表
     * @param omsOutboundDTO
     * @return
     */
    @Override
    public List<OmsOutboundVO> queryList(OmsOutboundDTO omsOutboundDTO){
        //排序字段名需要驼峰转数据库下划线类型字段名
        if(StringUtils.isNotEmpty(omsOutboundDTO.getSortField())){
            omsOutboundDTO.setSortField(CommonUtil.camel2Underline(omsOutboundDTO.getSortField()));
        }
        List<OmsOutboundVO> omsOutboundVOList = baseMapper.queryList(omsOutboundDTO);
        omsOutboundVOList.forEach(info ->{
            info.setTypeText(OmsOutboundTypeEnum.translate(info.getType()));
            //info.setWarehouseName(warehouseCache.translate(info.getWarehouseCode()));
            info.setStatusText(OmsOutboundStatusEnum.translate(info.getStatus()));

        });
        return omsOutboundVOList;
    }

    /**
     * 查询合计,包含总数
     * @param omsOutboundDTO
     * @return
     */
    @Override
    public OmsOutboundVO queryListSum(OmsOutboundDTO omsOutboundDTO){
        OmsOutboundVO omsOutboundVO = baseMapper.queryListSum(omsOutboundDTO);
        return omsOutboundVO;
    }

    /**
     * 根据销售单生成oms出库单
     *
     * @param sale
     * @param omsWmsFlag: 0: oms直接生成；1：wms转而生成
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void saveBySale(SaleVO sale, Integer omsWmsFlag) {
        String createdCode = UserUtil.getCode();
        String createdName = partyCache.translate(createdCode);
        // 生成主单
        OmsOutbound outbound = generateOmsOutbound(sale);
        // 保存主单
        save(outbound);
        // 生成明细 需要过滤掉销售单中数量为0的数据
        List<OmsOutboundItem> outboundItems = sale.getSaleItemList().stream().map(e->{
            OmsOutboundItem item =  generateOmsOutboundItem(outbound, e, createdCode, createdName);
            if (NUM_ZERO.equals(omsWmsFlag)) {
                item.setOutUnitQuantity(e.getPlanQuantity());
                item.setOutQuantity(e.getPlanBasicQuantity());
            } else {
                item.setOutUnitQuantity(e.getActualQuantity());
                item.setOutQuantity(e.getActualBasicQuantity());
            }

            return item;
        }).collect(Collectors.toList());

        // 保存明细
        iOmsOutboundItemService.saveBatch(outboundItems);

        // oms直接生成的，销售数量就是计划数量
        if (NUM_ZERO.equals(omsWmsFlag)) {
            // 更新销售单、明细的实际销售数量(销售单位、基本单位)，由于是oms操作，所以实际数量就是计划数量
            iSaleService.update(new UpdateWrapper<Sale>().lambda().eq(Sale::getCode, outbound.getSourceCode())
                    .setSql(" actual_quantity = plan_quantity ").setSql(" actual_basic_quantity = plan_basic_quantity "));
            iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, outbound.getSourceCode())
                    .setSql(" actual_quantity = plan_quantity ").setSql(" actual_basic_quantity = plan_basic_quantity "));
        }
        // 减少库存
        OmsInventory sameInv;
        for(OmsOutboundItem e: outboundItems){
            sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), outbound.getWarehouseCode(), outbound.getShipmentCode());
            iOmsInventoryService.adjustQuantity(sameInv.getCode(), e.getOutQuantity().negate());
        }
        // 减少占用数量
        /*for (SaleItemVO e: sale.getSaleItemList()) {
            sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), outbound.getWarehouseCode(), outbound.getShipmentCode());
            iOmsInventoryService.adjustUseQuantity(sameInv.getCode(), e.getPlanBasicQuantity().negate());
        }*/

        // 生成费用
        iOmsSettlementService.generateReceivable(outbound);
    }


    /**
     * wms端触发生成oms出库单
     * @param sale
     * @param wmsOutbound
     * @param sourceItemOutQuantity
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void saveBySaleForWms(SaleVO sale, Outbound wmsOutbound, Map<String, BigDecimal[]> sourceItemOutQuantity) {
        String createdCode = UserUtil.getCode();
        String createdName = partyCache.translate(createdCode);
        // 生成主单
        OmsOutbound outbound = generateOmsOutbound(sale);

        // 过滤销售单明细，只留下本次发货单明细关联的销售明细
        List<SaleItemVO> saleItems = sale.getSaleItemList().stream().filter(e->sourceItemOutQuantity.containsKey(e.getCode())).collect(Collectors.toList());

        BigDecimal sumTotalMoney = BigDecimal.ZERO;

        // 根据销售单明细生成出库单明细， 使用wms出库数量
        List<OmsOutboundItem> outboundItems = new ArrayList<>();
        for (SaleItemVO e: saleItems) {
            OmsOutboundItem item =  generateOmsOutboundItem(outbound, e, createdCode, createdName);
            item.setOutUnitQuantity(sourceItemOutQuantity.get(e.getCode())[NUM_ONE]);
            item.setOutQuantity(sourceItemOutQuantity.get(e.getCode())[NUM_ZERO]);
            //价税合计 = 含税单价*上架数量
            item.setTotalMoney(e.getTaxUnitPrice().multiply(e.getActualQuantity()).setScale(6, BigDecimal.ROUND_HALF_UP));

            outboundItems.add(item);

            // 计算明细价税合计总额
            sumTotalMoney = sumTotalMoney.add(item.getTotalMoney());
        }

        // 获取销售单剩余可用付款金额
        BigDecimal remainPayMoney = sale.getPaymentAmount();
        if (sale.getUsePaymentAmount() != null) {
            remainPayMoney = sale.getPaymentAmount().subtract(sale.getUsePaymentAmount());
        }

        // 本单会用到的剩余可用付款金额量
        BigDecimal useMoney = remainPayMoney;
        // 如果剩余已付款数量大于这单总欠款数量，则用到的金额为本单总欠款量
        if (remainPayMoney.compareTo(sumTotalMoney) >= 0) {
            useMoney = sumTotalMoney;
        }

        outbound.setPaymentAmount(useMoney);

        // 更新主单欠款金额，以实际出库信息总和为准，因为可能存在发货单拆分为多个出库单的情况
        outbound.setOwedAmount(sumTotalMoney.subtract(useMoney));

        // 如果本次用了可用付款金额，则需要更新销售单的已用付款金额量
        if (useMoney.compareTo(BigDecimal.ZERO) > NUM_ZERO) {
            // 更新采购单中使用付款金额
            iSaleService.update(new UpdateWrapper<Sale>().lambda().eq(Sale::getCode, sale.getCode())
                    .setSql("use_payment_amount = use_payment_amount + " + useMoney));
        }

        // 保存主单
        save(outbound);

        // 保存明细
        iOmsOutboundItemService.saveBatch(outboundItems);

        // 减少库存
        OmsInventory sameInv;
        for(OmsOutboundItem e: outboundItems){
            sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), outbound.getWarehouseCode(), outbound.getShipmentCode());
            iOmsInventoryService.adjustQuantity(sameInv.getCode(), e.getOutQuantity().negate());
        }

        // 生成费用
        iOmsSettlementService.generateReceivable(outbound);
    }


    /**
     * 根据销售单生成oms出库单主单
     * @param sale
     * @return
     */
    private OmsOutbound generateOmsOutbound(SaleVO sale) {
        OmsOutbound outbound = new OmsOutbound();
        BeanUtil.copyProperties(sale, outbound);
        // 添加前缀，用于区分是oms的接口生成的
        outbound.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.CK, outbound.getShipmentCode(),UserUtil.getBranchCode()));
        outbound.setSourceCode(sale.getCode());
        outbound.setType(OmsOutboundTypeEnum.SALE_OUT.getValue());
        outbound.setStatus(OmsOutboundStatusEnum.FINISH.getValue());
        // 避免sale污染备注字段
        outbound.setRemarks(StrUtil.EMPTY);
        outbound.setDeleted(0);
        return outbound;
    }


    /**
     * @param code :
     * @description 获取出库单及出库单明细列表
     * @author xuyang
     * @date 2020/11/14 8:02
     **/
    @Override
    public OmsOutboundVO getVOByCode(String code) {
        OmsOutbound entity = this.getOne(new QueryWrapper<OmsOutbound>().lambda().eq(OmsOutbound::getCode, code));
        OmsOutboundVO vo = new OmsOutboundVO();
        BeanUtil.copyProperties(entity, vo);
        // 翻译
        vo.setTypeText(OmsOutboundTypeEnum.translate(vo.getType()));
        vo.setGroupName(partyGroupCache.translate(vo.getGroupCode()));

        OmsOutboundItemDTO itemDTO = new OmsOutboundItemDTO();
        itemDTO.setOutboundCode(vo.getCode());
        itemDTO.setPageSize(Integer.MAX_VALUE);
        List<OmsOutboundItemVO> itemVOS = iOmsOutboundItemService.queryList(itemDTO);
        vo.setOmsOutboundItemVOList(itemVOS);
        vo.setWarehouseName(warehouseCache.translate(vo.getWarehouseCode()));
        vo.setCustomerName(customerCache.translate(vo.getCustomerCode()));
        return vo;
    }

    /**
     * @param saleReturnVO :
     * @param omsWmsFlag: 0: 直接根据oms销退单生成，出库数量是销退单明细计划数量；
     *                  1： wms更新oms生成，出库数量为销退单明细实际出库数量
     * @param putaway : 采用上架单明细的上架数量回填出库数量
     * 根据销售退货单生成红冲出库单
     * @author xuyang
     * @date 2020/12/14 15:31
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void saveBySaleReturn(OmsSaleReturnVO saleReturnVO, Integer omsWmsFlag, Putaway putaway) {
        // 保存出库单主单
        OmsOutbound outbound = new OmsOutbound();
        BeanUtil.copyProperties(saleReturnVO, outbound);
        outbound.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.CK, "",UserUtil.getBranchCode()));
        outbound.setSourceCode(saleReturnVO.getCode());
        outbound.setType(OmsOutboundTypeEnum.SALE_RETURN.getValue());
        outbound.setStatus(OmsOutboundStatusEnum.FINISH.getValue());
        outbound.setRemarks(StrUtil.EMPTY);
        outbound.setCreatedBy(StrUtil.EMPTY);
        outbound.setModifiedBy(StrUtil.EMPTY);
        //注意
        outbound.setPaymentAmount(BigDecimal.ZERO);
        BigDecimal owedAmount=BigDecimal.ZERO;


        // 生成出库明细，并保存
        List<OmsOutboundItem> outboundItems  = new ArrayList<>();
        for(OmsSaleReturnItemVO e: saleReturnVO.getSaleReturnItemList()) {
            OmsOutboundItem item = new OmsOutboundItem();
            BeanUtil.copyProperties(e, item);
            item.setCode(IdUtil.simpleUUID());
            item.setOutboundCode(outbound.getCode());
            item.setSourceItemCode(e.getCode());
            item.setStatus(OmsOutboundStatusEnum.FINISH.getValue());
            item.setRemarks(StrUtil.EMPTY);
            item.setCreatedBy(StrUtil.EMPTY);
            item.setModifiedBy(StrUtil.EMPTY);
            item.setDeleted(NUM_ZERO);

            if (NUM_ZERO.equals(omsWmsFlag)) {
                // 销售退单出库数量为oms计划数量的负数，用于红冲
                item.setOutQuantity(e.getPlanBasicQuantity().negate());
                item.setOutUnitQuantity(e.getPlanQuantity().negate());
                owedAmount = owedAmount.add(e.getUnitPrice().multiply(e.getPlanQuantity()).add(
                        e.getUnitPrice().multiply(e.getPlanQuantity()).multiply(e.getTaxRate()).divide(DECIMAL100))
                        .setScale(6, BigDecimal.ROUND_HALF_UP).negate());
                outboundItems.add(item);
            } else {
                // 红冲的单据，用上架明细数据生成出库数据，需要判断实际出库数量是否>0，如果有数据，则可生成
                if (ObjectUtil.isNotNull(putaway)) {
                    List<PutawayItem> putawayItems = putawayItemService.list(new QueryWrapper<PutawayItem>().lambda().eq(PutawayItem::getPutawayCode,putaway.getCode()));
                    for (PutawayItem putawayItem : putawayItems) {
                        if (e.getCode().equals(putawayItem.getSourceItemCode())) {
                            if (ObjectUtil.isNotNull(putawayItem.getPutawayQuantity()) && putawayItem.getPutawayQuantity().compareTo(BigDecimal.ZERO) >0 ) {
                                // 出库数量为实际数量
                                item.setOutQuantity(putawayItem.getPutawayQuantity());
                                item.setOutUnitQuantity(putawayItem.getPutawayQuantity());
                                owedAmount = owedAmount.add(e.getUnitPrice().multiply(e.getActualQuantity()).add(
                                        e.getUnitPrice().multiply(e.getActualQuantity()).multiply(e.getTaxRate()).divide(DECIMAL100))
                                        .setScale(6, BigDecimal.ROUND_HALF_UP).negate());
                                outboundItems.add(item);
                            }
                        }
                    }
                }
            }
        }

        iOmsOutboundItemService.saveBatch(outboundItems);
        outbound.setOwedAmount(owedAmount);
        save(outbound);
        // oms直接生成出库单时，oms销退单实际数量还没更新，需要出库单更新一下
        if (NUM_ZERO.equals(omsWmsFlag)) {
            // 出库单回填销售退单
            refreshSaleReturnOutQuantity(saleReturnVO.getCode());
        }
        // 调整库存
//        OmsInventory sameInv;
        for(OmsOutboundItem e: outboundItems){
//            sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), outbound.getWarehouseCode(), outbound.getShipmentCode());
//            iOmsInventoryService.adjustQuantity(sameInv.getCode(), e.getOutQuantity().negate());

            // 销退入库，库存价格发生变化，调整库存数量、重新计算库存的均价、总价
            iOmsInventoryService.update(new UpdateWrapper<OmsInventory>().lambda()
                    .eq(OmsInventory::getWarehouseCode, outbound.getWarehouseCode())
                    .eq(OmsInventory::getShipmentCode, outbound.getShipmentCode())
                    .eq(OmsInventory::getGoodsCode, e.getGoodsCode())
                    .setSql(" quantity = quantity + " + e.getOutQuantity())
                    .setSql(" average_unit_price = (total_amount+" + e.getTotalMoney()+")/quantity ")
                    .setSql(" total_amount = average_unit_price * quantity ")
            );
        }


        // 生成费用
        iOmsSettlementService.generateReceivable(outbound);
    }

    @Override
    public void saveByOmsAllocation(OmsAllocation omsAllocation) {
        OmsOutbound omsOutbound = new OmsOutbound();
        BeanUtils.copyProperties(omsAllocation, omsOutbound);
        omsOutbound.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.CK, "",UserUtil.getBranchCode()));
        omsOutbound.setWarehouseCode(omsAllocation.getSourceWarehouseCode());
        omsOutbound.setSourceCode(omsAllocation.getCode());
        omsOutbound.setStatus(OmsOutboundStatusEnum.FINISH.getValue());
        omsOutbound.setType(OmsOutboundTypeEnum.ALLOCATION.getValue());
        omsOutbound.setCreatedBy(UserUtil.getCode());
        omsOutbound.setCreatedName(partyCache.translate(UserUtil.getCode()));
        omsOutbound.setGmtCreated(LocalDateTime.now());

        save(omsOutbound);

        List<OmsOutboundItem> omsOutboundItemList = CollectionUtil.newArrayList();
        omsAllocation.getAllocationItemList().stream().forEach(item -> {
            //出库数量为零的时候，不需要回显数据
            if (item.getUnitOutboundQuantity().compareTo(BigDecimal.ZERO)>0){
                //调拨单生成的入库单  单价和含税单价 一样，都用调拨单的平均价表示
                BigDecimal unitPrice = item.getAverageUnitPrice();
                BigDecimal outQuantity = item.getOutboundQuantity();
                //调拨单生成的入库单  由于税率为0，金额和价税合计 一样，都采用 amountMoney 的值
                BigDecimal amountMoney = unitPrice.multiply(outQuantity).setScale(6, BigDecimal.ROUND_HALF_UP);
                OmsOutboundItem omsOutboundItem = new OmsOutboundItem();
                BeanUtils.copyProperties(item, omsOutboundItem);
                omsOutboundItem.setCode(IdUtil.simpleUUID());
                omsOutboundItem.setOutboundCode(omsOutbound.getCode());
                omsOutboundItem.setSourceItemCode(item.getCode());
                omsOutboundItem.setOutQuantity(item.getOutboundQuantity());
                omsOutboundItem.setOutUnitQuantity(item.getUnitOutboundQuantity());
                omsOutboundItem.setCreatedBy(UserUtil.getCode());
                omsOutboundItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
                omsOutboundItem.setGmtCreated(LocalDateTime.now());

                omsOutboundItem.setTaxRate(BigDecimal.ZERO);
                omsOutboundItem.setUnitPrice(unitPrice);
                omsOutboundItem.setTaxUnitPrice(unitPrice);
                omsOutboundItem.setAmountMoney(amountMoney);
                omsOutboundItem.setTaxMoney(BigDecimal.ZERO);
                omsOutboundItem.setTotalMoney(amountMoney);
                omsOutboundItemList.add(omsOutboundItem);
            }
        });
        iOmsOutboundItemService.saveBatch(omsOutboundItemList);

        omsOutboundItemList.forEach(e -> {
            OmsInventory sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), omsOutbound.getWarehouseCode(), omsOutbound.getShipmentCode());
            if (ObjectUtil.isNotNull(sameInv)) {
                iOmsInventoryService.adjustQuantityAndPrice(sameInv.getCode(), e.getOutQuantity().negate());
            }
        });

    }

    @Override
    public void saveByWmsOutbound(Outbound outbound, List<OutboundItem> outboundItemList) {
        String groupCode = outbound.getGroupCode();
        List<WarehouseShipment> warehouseShipmentCacheList = warehouseShipmentCache.getObjList(outbound.getWarehouseCode());
        List<WarehouseShipment> warehouseShipmentList = warehouseShipmentCacheList.stream().filter(item -> item.getShipmentCode().equals(outbound.getShipmentCode())).collect(Collectors.toList());
        if (IterUtil.isNotEmpty(warehouseShipmentList) && warehouseShipmentList.get(NUM_ZERO).getUseOms().equals(NUM_ONE)) {
            OmsOutbound omsOutbound = new OmsOutbound();
            BeanUtils.copyProperties(outbound, omsOutbound);
            omsOutbound.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.CK, "",groupCode));
            omsOutbound.setSourceCode(outbound.getSourceCode());
            omsOutbound.setStatus(OmsOutboundStatusEnum.FINISH.getValue());
            DeliveryTypeEnum outTypeEnum = DeliveryTypeEnum.getEnumByValue(outbound.getType());
            switch (outTypeEnum) {
                case OUT_BAOSUN:
                    omsOutbound.setType(OmsOutboundTypeEnum.DAMAGED.getValue());
                    break;
                case OUT_LINGYONG:
                    omsOutbound.setType(OmsOutboundTypeEnum.COLLECT.getValue());
                    break;
                case OUT_LOSS:
                    omsOutbound.setType(OmsOutboundTypeEnum.COUNT_LOSS.getValue());
                    break;
                default:
                    // 其他情况则不生成oms出库单
                    return;
            }
            omsOutbound.setCreatedBy(UserUtil.getCode());
            omsOutbound.setCreatedName(partyCache.translate(UserUtil.getCode()));
            omsOutbound.setGmtCreated(LocalDateTime.now());

            List<OmsOutboundItem> omsOutboundItemList = outboundItemList.stream().map(item -> {
                OmsOutboundItem omsOutboundItem = new OmsOutboundItem();
                BeanUtils.copyProperties(item, omsOutboundItem);
                omsOutboundItem.setCode(IdUtil.simpleUUID());
                omsOutboundItem.setOutboundCode(omsOutbound.getCode());
                omsOutboundItem.setOutQuantity(item.getOutboundQuantity());
                omsOutboundItem.setSourceItemCode(item.getSourceItemCode());
                omsOutboundItem.setCreatedBy(UserUtil.getCode());
                omsOutboundItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
                omsOutboundItem.setGmtCreated(LocalDateTime.now());

                return omsOutboundItem;
            }).collect(Collectors.toList());

            save(omsOutbound);
            iOmsOutboundItemService.saveBatch(omsOutboundItemList);

            omsOutboundItemList.forEach(e -> {
                OmsInventory sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), omsOutbound.getWarehouseCode(), omsOutbound.getShipmentCode());
                if (ObjectUtil.isNotNull(sameInv)) {
                    iOmsInventoryService.adjustQuantityAndPrice(sameInv.getCode(), e.getOutQuantity().negate());
                }
            });
        }

    }

    /**
     * 根据调拨单生成出库单
     *
     * @param allocation
     * @param wmsOut
     * @param sourceItemOutQuantity
     */
    @Override
    public void saveByOmsAllocationForWms(OmsAllocation allocation, Outbound wmsOut, Map<String, BigDecimal[]> sourceItemOutQuantity) {
        OmsOutbound omsOutbound = new OmsOutbound();
        BeanUtils.copyProperties(allocation, omsOutbound);
        omsOutbound.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.CK, "",UserUtil.getBranchCode()));
        omsOutbound.setWarehouseCode(allocation.getSourceWarehouseCode());
        omsOutbound.setSourceCode(allocation.getCode());
        omsOutbound.setStatus(OmsOutboundStatusEnum.FINISH.getValue());
        omsOutbound.setType(OmsOutboundTypeEnum.ALLOCATION.getValue());
        omsOutbound.setCreatedBy(UserUtil.getCode());
        omsOutbound.setCreatedName(partyCache.translate(UserUtil.getCode()));
        omsOutbound.setGmtCreated(LocalDateTime.now());

        save(omsOutbound);

        // 根据本次出库明细过滤出本次调拨单明细
        List<OmsAllocationItem> allocationItems = allocation.getAllocationItemList();
        allocationItems = allocationItems.stream().filter(e->sourceItemOutQuantity.containsKey(e.getCode())).collect(Collectors.toList());

        // 生成oms出库单明细列表
        List<OmsOutboundItem> outboundItems = allocationItems.stream().map(e->{
            OmsOutboundItem omsOutboundItem = new OmsOutboundItem();
            BeanUtils.copyProperties(e, omsOutboundItem);
            omsOutboundItem.setCode(IdUtil.simpleUUID());
            omsOutboundItem.setOutboundCode(omsOutbound.getCode());
            omsOutboundItem.setSourceItemCode(e.getCode());
            // 调整实际出库数量
            omsOutboundItem.setOutQuantity(sourceItemOutQuantity.get(e.getCode())[NUM_ZERO]);
            omsOutboundItem.setOutUnitQuantity(sourceItemOutQuantity.get(e.getCode())[NUM_ONE]);
            omsOutboundItem.setCreatedBy(UserUtil.getCode());
            omsOutboundItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
            omsOutboundItem.setGmtCreated(LocalDateTime.now());
            return omsOutboundItem;
        }).collect(Collectors.toList());

        iOmsOutboundItemService.saveBatch(outboundItems);

        // 扣减库存
        outboundItems.forEach(e -> {
            OmsInventory sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), omsOutbound.getWarehouseCode(), omsOutbound.getShipmentCode());
            if (ObjectUtil.isNotNull(sameInv)) {
                iOmsInventoryService.adjustQuantityAndPrice(sameInv.getCode(), e.getOutQuantity().negate());
            }
        });
    }

    @Override
    public void saveByOmsAllocationForWmsOutBound(OmsAllocation allocation, Outbound wmsOut) {
        OmsOutbound omsOutbound = new OmsOutbound();
        BeanUtils.copyProperties(allocation, omsOutbound);
        omsOutbound.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.CK, "",UserUtil.getBranchCode()));
        omsOutbound.setWarehouseCode(allocation.getSourceWarehouseCode());
        omsOutbound.setSourceCode(allocation.getCode());
        omsOutbound.setStatus(OmsOutboundStatusEnum.FINISH.getValue());
        omsOutbound.setType(OmsOutboundTypeEnum.ALLOCATION.getValue());
        omsOutbound.setCreatedBy(UserUtil.getCode());
        omsOutbound.setCreatedName(partyCache.translate(UserUtil.getCode()));
        omsOutbound.setGmtCreated(LocalDateTime.now());

        save(omsOutbound);

        // 根据本次出库明细过滤出本次调拨单明细
        List<OutboundItem> outboundItem = iOutboundItemService.list(new QueryWrapper<OutboundItem>().lambda().eq(OutboundItem::getOutboundCode, wmsOut.getCode()));

        // 生成oms出库单明细列表
        List<OmsOutboundItem> outboundItems = outboundItem.stream().map(e->{
            OmsOutboundItem omsOutboundItem = new OmsOutboundItem();
            BeanUtils.copyProperties(e, omsOutboundItem);
            omsOutboundItem.setCode(IdUtil.simpleUUID());
            omsOutboundItem.setOutboundCode(omsOutbound.getCode());
            omsOutboundItem.setSourceItemCode(e.getCode());
            // 调整实际出库数量
            omsOutboundItem.setOutQuantity(e.getOutboundQuantity());
            omsOutboundItem.setOutUnitQuantity(e.getOutboundUnitQuantity());
            omsOutboundItem.setCreatedBy(UserUtil.getCode());
            omsOutboundItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
            omsOutboundItem.setGmtCreated(LocalDateTime.now());
            return omsOutboundItem;
        }).collect(Collectors.toList());

        iOmsOutboundItemService.saveBatch(outboundItems);

        // 扣减库存
        outboundItems.forEach(e -> {
            OmsInventory sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), omsOutbound.getWarehouseCode(), omsOutbound.getShipmentCode());
            if (ObjectUtil.isNotNull(sameInv)) {
                iOmsInventoryService.adjustQuantityAndPrice(sameInv.getCode(), e.getOutQuantity().negate());
            }
        });
    }



    /**
     * @description
     * wms实际入库后，销售退单实际数量可能与计划数量不一样，需要调整一下销售单中占用的退货数量
     * @author xuyang
     * @date 2020/12/16 20:18
     * @param saleReturnVO:
     **/
    @Transactional(rollbackFor = {Exception.class})
    public void adjustSaleActualRefundQuantity(OmsSaleReturnVO saleReturnVO) {
        saleReturnVO.getSaleReturnItemList().forEach(e->{
            // 获取 实际数量-计划数量的差值
            BigDecimal diffQuantity = e.getActualQuantity().subtract(e.getPlanQuantity());
            BigDecimal diffBasicQuantity = e.getActualBasicQuantity().subtract(e.getPlanBasicQuantity());
            // 差值只能 <= 0 ; 等于0不做处理
            if (diffBasicQuantity.compareTo(BigDecimal.ZERO) < 0) {
                iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getCode, e.getSaleItemCode())
                    .setSql(" refund_quantity = refund_quantity + " + diffQuantity)
                    .setSql(" refund_basic_quantity = refund_basic_quantity + " + diffBasicQuantity));
            }
        });

    }

    /**
     * @description
     * 出库单生成后，回填销售退单的出库数量信息
     * @author xuyang
     * @date 2020/12/14 16:48
     * @param saleReturnCode:
     **/
    @Transactional(rollbackFor = {Exception.class})
    public void refreshSaleReturnOutQuantity(String saleReturnCode) {
        // 更新销退单明细实际数量
        iOmsSaleReturnItemService.updateOutQuantity(saleReturnCode);
        // 更新销退单主单实际数量
        iOmsSaleReturnService.updateOutQuantityByItems(saleReturnCode);
    }

    /**
     * wms出库后同步更新oms库存量
     * @param omsOutbound
     * @param saleItems
     * @param allStatusQuantity
     * @param contUseStatusQuantity
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateInvForWmsOut(OmsOutbound omsOutbound, List<SaleItem> saleItems, Map<String, BigDecimal> allStatusQuantity, Map<String, BigDecimal> contUseStatusQuantity){
        OmsInventory sameInv;
        // 更新库存量， 根据商品code 减去库存实际出库量、出库时使用的不可出状态库存量
        // 不可用库存量 = 原数据 - 不可出库使用量
        // 总量 = 原数据 - 总出库量
        for(String goodsCode : allStatusQuantity.keySet()){
            BigDecimal allQuantity = allStatusQuantity.get(goodsCode);
            BigDecimal contUseQuantity = contUseStatusQuantity.getOrDefault(goodsCode, BigDecimal.ZERO);
            sameInv = iOmsInventoryService.hasSameInventory(goodsCode, omsOutbound.getWarehouseCode(), omsOutbound.getShipmentCode());
            iOmsInventoryService.update(new UpdateWrapper<OmsInventory>().lambda().eq(OmsInventory::getCode, sameInv.getCode())
                    .setSql(" cont_use_quantity = cont_use_quantity - " + contUseQuantity)
                    .setSql(" quantity = quantity - " + allQuantity));
        }

        // 再释放锁定库存，占用库存量 = 原数据 - 销售明细量（即全部释放）： 由于单据已完结，所以将所有锁定的全部释放掉（不管是实际已出库了，还是没出库需要释放的）
        for(SaleItem e: saleItems){
            sameInv = iOmsInventoryService.hasSameInventory(e.getGoodsCode(), omsOutbound.getWarehouseCode(), omsOutbound.getShipmentCode());
            iOmsInventoryService.update(new UpdateWrapper<OmsInventory>().lambda().eq(OmsInventory::getCode, sameInv.getCode())
                    .setSql(" use_quantity = use_quantity - " + e.getPlanBasicQuantity()));
        }
    }

    /**
     * 根据wms出库明细，计算各种分组类型的出库数量
     * @param outboundItems
     * @param itemQuantity 根据销售单明细号分组
     * @param allStatusQuantity 根据商品code分组
     * @param contUseStatusQuantity 根据商品code且不可出库状态分组
     */
    private void calcWmsOutQuantity(List<OutboundItem> outboundItems, Map<String, BigDecimal> itemQuantity, Map<String, BigDecimal> allStatusQuantity,
                                    Map<String, BigDecimal> contUseStatusQuantity){
        BigDecimal zero = BigDecimal.ZERO;
        outboundItems.forEach(e->{
            String code = e.getGoodsCode();
            String itemCode = e.getSaleItemCode();
            BigDecimal quantity = e.getOutboundQuantity();
            String flag = e.getIsConsignment();
            //　根据销售单明细分组计算明细实际出库量
            itemQuantity.put(itemCode, itemQuantity.getOrDefault(itemCode, BigDecimal.ZERO).add(e.getOutboundQuantity()));
            // 根据商品，计算商品实际出库量
            allStatusQuantity.put(code, allStatusQuantity.getOrDefault(allStatusQuantity.get(code), zero).add(quantity));
            // 计算不可出状态的商品出库量，用于扣减库存不可出数量
            if(StrUtil.equals(flag, NumConst.NUM_ONE.toString())){
                contUseStatusQuantity.put(code, contUseStatusQuantity.getOrDefault(contUseStatusQuantity.get(code), zero).add(quantity));
            }
        });
    }

    /**
     * 根据销售单明细生成出库单明细
     * @param omsOutbound
     * @param saleItem
     * @param createdCode
     * @param createdName
     * @return
     */
    private OmsOutboundItem generateOmsOutboundItem(OmsOutbound omsOutbound, SaleItemVO saleItem, String createdCode, String createdName){
        OmsOutboundItem omsOutboundItem = new OmsOutboundItem();
        BeanUtil.copyProperties(saleItem, omsOutboundItem);
        omsOutboundItem.setCode(IdUtil.simpleUUID());
        omsOutboundItem.setSourceItemCode(saleItem.getCode());
        omsOutboundItem.setOutboundCode(omsOutbound.getCode());
        omsOutboundItem.setStatus(StrUtil.EMPTY);
        omsOutboundItem.setRemarks(StrUtil.EMPTY);
        omsOutboundItem.setCreatedBy(createdCode);
        omsOutboundItem.setCreatedName(createdName);
        omsOutboundItem.setModifiedBy(StrUtil.EMPTY);
        omsOutboundItem.setModifiedName(StrUtil.EMPTY);
        omsOutboundItem.setGmtModified(null);
        return omsOutboundItem;
    }

    @Override
    public List<Map<String, Object>> outboundCount(String shipmentCode, List<String> warehouseCodes) {
        return baseMapper.outboundCount(shipmentCode, warehouseCodes);
    }

}
